This installs pyfme
to be run in this online notebook
In [ ]:
!pip install git+https://github.com/AeroPython/PyFME.git
In order to perform a simulation, the first thing we need is an aircraft:
In [1]:
from pyfme.aircrafts import Cessna172
In [2]:
aircraft = Cessna172()
Aircraft will provide the simulator the forces, moments and inertial properties in order to perform the integration of the dynamic system equations:
In [3]:
print(f"Aircraft mass: {aircraft.mass} kg")
print(f"Aircraft inertia tensor: \n {aircraft.inertia} kg/m²")
In [4]:
print(f"forces: {aircraft.total_forces} N")
print(f"moments: {aircraft.total_moments} N·m")
For the aircraft, in order to calculate its forces and moments it is necessary to set the controls values within the limits:
In [5]:
print(aircraft.controls)
In [6]:
print(aircraft.control_limits)
but also to provide and environment (ie. atmosphere, winds, gravity) and the aircraft state, which will also determine the aerodynamic contribution.
In [7]:
from pyfme.environment.atmosphere import ISA1976
from pyfme.environment.wind import NoWind
from pyfme.environment.gravity import VerticalConstant
In [8]:
atmosphere = ISA1976()
gravity = VerticalConstant()
wind = NoWind()
The atmosphere, wind and gravity model make up the environment:
In [9]:
from pyfme.environment import Environment
In [10]:
environment = Environment(atmosphere, gravity, wind)
The environment has an update method which given the state (ie. position, altitude...) updates the environment variables (ie. density, wind magnitude, gravity force...)
In [11]:
help(environment.update)
Even if the state can be set manually by giving the position, attitude, velocity, angular velocities... Most of the times, the user will want to trim the aircraft in a stationary condition. The aircraft controls to flight in that condition will be also provided by the trimmer.
In [12]:
from pyfme.utils.trimmer import steady_state_trim
In [13]:
help(steady_state_trim)
In [14]:
from pyfme.models.state.position import EarthPosition
In [15]:
pos = EarthPosition(x=0, y=0, height=1000)
psi = 0.5 # rad
TAS = 45 # m/s
controls0 = {'delta_elevator': 0, 'delta_aileron': 0, 'delta_rudder': 0, 'delta_t': 0.5}
In [16]:
trimmed_state, trimmed_controls = steady_state_trim(
aircraft,
environment,
pos,
psi,
TAS,
controls0
)
In [17]:
trimmed_state
Out[17]:
In [18]:
trimmed_controls
Out[18]:
Now, all the necessary elements in order to calculate forces and moments are available
In [19]:
# Environment conditions for the current state:
environment.update(trimmed_state)
# Forces and moments calculation:
forces, moments = aircraft.calculate_forces_and_moments(trimmed_state, environment, controls0)
In [20]:
forces, moments
Out[20]:
The aircraft is trimmed indeed: the total forces and moments (aerodynamics + gravity + thrust) are zero
In order to simulate the dynamics of the aircraft under certain inputs in an environment, the user can set up a simulation using a dynamic system:
In [21]:
from pyfme.models import EulerFlatEarth
In [22]:
system = EulerFlatEarth(t0=0, full_state=trimmed_state)
Let's set the controls for the aircraft during the simulation. As a first step we will set them constant and equal to the trimmed values.
In [23]:
from pyfme.utils.input_generator import Constant
In [24]:
controls = controls = {
'delta_elevator': Constant(trimmed_controls['delta_elevator']),
'delta_aileron': Constant(trimmed_controls['delta_aileron']),
'delta_rudder': Constant(trimmed_controls['delta_rudder']),
'delta_t': Constant(trimmed_controls['delta_t'])
}
In [25]:
from pyfme.simulator import Simulation
In [26]:
sim = Simulation(aircraft, system, environment, controls)
Once the simulation is set, the propagation can be performed:
In [27]:
results = sim.propagate(10)
The results are returned in a DataFrame:
In [28]:
results
Out[28]:
In [29]:
%matplotlib inline
In [30]:
kwargs = {'marker': '.',
'subplots': True,
'sharex': True,
'figsize': (12, 6)}
In [31]:
results.plot(y=['x_earth', 'y_earth', 'height'], **kwargs);
In [32]:
results.plot(y=['psi', 'theta', 'phi'], **kwargs);
In [33]:
results.plot(y=['v_north', 'v_east', 'v_down'], **kwargs);
In [34]:
results.plot(y=['p', 'q', 'r'], **kwargs);
In [35]:
results.plot(y=['alpha', 'beta', 'TAS'], **kwargs);
In [36]:
results.plot(y=['Fx', 'Fy', 'Fz'], **kwargs);
In [37]:
results.plot(y=['Mx', 'My', 'Mz'], **kwargs);
In [38]:
results.plot(y=['elevator', 'aileron', 'rudder', 'thrust'], **kwargs);
Let's set the controls for the aircraft during the simulation. As a first step we will set them constant and equal to the trimmed values.
In [39]:
from pyfme.utils.input_generator import Doublet
In [40]:
de0 = trimmed_controls['delta_elevator']
In [41]:
controls = controls = {
'delta_elevator': Doublet(t_init=2, T=1, A=0.1, offset=de0),
'delta_aileron': Constant(trimmed_controls['delta_aileron']),
'delta_rudder': Constant(trimmed_controls['delta_rudder']),
'delta_t': Constant(trimmed_controls['delta_t'])
}
In [42]:
sim = Simulation(aircraft, system, environment, controls)
Once the simulation is set, the propagation can be performed:
In [43]:
results = sim.propagate(90)
In [44]:
results.plot(y=['x_earth', 'y_earth', 'height'], **kwargs);
In [45]:
results.plot(y=['psi', 'theta', 'phi'], **kwargs);
In [46]:
results.plot(y=['v_north', 'v_east', 'v_down'], **kwargs);
In [47]:
results.plot(y=['p', 'q', 'r'], **kwargs);
In [48]:
results.plot(y=['alpha', 'beta', 'TAS'], **kwargs);
In [49]:
results.plot(y=['Fx', 'Fy', 'Fz'], **kwargs);
In [50]:
results.plot(y=['Mx', 'My', 'Mz'], **kwargs);
In [51]:
results.plot(y=['elevator', 'aileron', 'rudder', 'thrust'], **kwargs);
In [52]:
dt = 0.05 # seconds
sim = Simulation(aircraft, system, environment, controls, dt)
In [53]:
results = sim.propagate(0.5)
results
Out[53]:
We can propagate for one time step even once the simulation has been propagated before:
In [54]:
results = sim.propagate(sim.time+dt)
results
Out[54]:
Notice that results
will include the previous timesteps as well as the last one. To get just the last one one can use pandas loc
or iloc
:
In [55]:
results.iloc[-1] # last time step
Out[55]:
In [56]:
results.loc[sim.time] # results for current simulation time
Out[56]: